package org.gap.wizards;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import net.slashie.libjcsi.CharKey;
import net.slashie.libjcsi.ConsoleSystemInterface;
import net.slashie.libjcsi.wswing.WSwingConsoleInterface;

import org.gap.jseed.Builder;
import org.gap.jseed.SeedContainer;
import org.gap.jseed.SeedContainerFactory;
import org.gap.wizards.creature.TargetCreatureSelecor;
import org.gap.wizards.creature.Creature.Attribute;
import org.gap.wizards.pc.Player;
import org.gap.wizards.pc.PlayerCharacter;
import org.gap.wizards.pc.skills.BasicSkill;
import org.gap.wizards.state.Condition;
import org.gap.wizards.state.TransitionState;
import org.gap.wizards.state.TransitionTracker;
import org.gap.wizards.utils.DiceBag;
import org.gap.wizards.utils.FlatFilePersistence;
import org.gap.wizards.utils.PersistenceUtils;
import org.gap.wizards.utils.SkillRollDice;
import org.gap.wizards.utils.SlashieConsole;
import org.gap.wizards.views.InventorySelectionView;
import org.gap.wizards.views.LibJCsiInventorySelectionView;
import org.gap.wizards.views.LibJCsiSelectSpellView;
import org.gap.wizards.views.MessageOutput;
import org.gap.wizards.views.QuickView;
import org.gap.wizards.views.SpellSelectionView;
import org.gap.wizards.views.StatusView;
import org.gap.wizards.views.ViewPlatform;
import org.gap.wizards.world.Location;
import org.gap.wizards.world.MapContainer;
import org.gap.wizards.world.TimerState;
import org.gap.wizards.world.World;
import org.gap.wizards.world.WorldTimer;
import org.gap.wizards.world.actions.Factory;
import org.gap.wizards.world.builder.WorldBuilder;

public class Start {
	private final class PlayerDeath implements Condition {
		private final SeedContainer store;

		private PlayerDeath(SeedContainer store) {
			this.store = store;
		}

		@Override
		public boolean isCondition(char keyPress) {
			return store.get(PlayerCharacter.class).getAttribute(Attribute.Hitpoints) < 1;
		}
	}

	public static final int SCREEN_WIDTH = 120;
	public static final int SCREEN_HEIGHT = 40;

	private static boolean isClosed = false;
	private static String title = "Wizards School - V 0.01";
	private final ConsoleSystemInterface csi = new WSwingConsoleInterface(title);
	private ViewPlatform console = new SlashieConsole(csi);
	private static List<GameListener> listeners = new ArrayList<GameListener>();
	private TransitionTracker tracker;

	public static void main(String[] p) throws IOException {
		System.setProperty("org.gap.jseed.ClassNameService", RegistryNamingService.class.getName());
		if (System.getProperty("save.dir") == null) {
			System.setProperty("save.dir", System.getProperty("user.home"));
		}
		WSwingConsoleInterface.ydim = SCREEN_HEIGHT;
		WSwingConsoleInterface.xdim = SCREEN_WIDTH;
		new Start().run();
	}

	public static void addGameListener(GameListener gameListener) {
		listeners.add(gameListener);
	}

	public Start() throws IOException {
		SeedContainer store = SeedContainerFactory.newContainer();
		store.bind(ConsoleSystemInterface.class, csi);
		setupTransitionStates(store);
		Factory.set(store);
	}

	private void setupTransitionStates(final SeedContainer store) throws IOException {
		tracker = new TransitionTracker();
		store.bind(TimerState.class, WorldTimer.class);

		store.bind(TransitionTracker.class, tracker);
		createWorld(store);

		TransitionState startGame = store.create(IntroductionState.class);
		InGameState inGame = store.create(InGameState.class);
		TransitionState inventory = store.create(InventoryViewState.class);
		TransitionState endGame = store.create(EndGame.class);
		TransitionState shutdown = store.create(ShutdownGame.class);
		TransitionState playerDied = store.create(PlayerDied.class);
		TransitionState shutdownWithoutSave = store.create(ShutdownWithoutSave.class);
		
		tracker.addTransition(startGame, new Condition.KeyPress(' '), inGame);
		tracker.addTransition(inGame, new Condition.KeyPress('Q'), endGame);
		tracker.addTransition(inGame, new Condition.KeyPress('i'), inventory);
		tracker.addTransition(inventory, new Condition.KeyPress('q'), inGame);
		tracker.addTransition(endGame, new Condition.KeyPress('y'), shutdown);
		tracker.addTransition(endGame, new Condition.KeyPress('n'), inGame);
		tracker.addTransition(inGame, new PlayerDeath(store), playerDied);
		tracker.addTransition(playerDied, new PlayerDeath(store), shutdownWithoutSave);
		tracker.setStart(startGame);

		bindViews(store, inGame);
	}

	private void bindViews(final SeedContainer store, InGameState inGame) {
		store.bind(ViewPlatform.class, console);
		inGame.addDrawableView(store.create(StatusView.class));
		inGame.addDrawableView(store.create(QuickView.class));
		store.bind(InventorySelectionView.class, LibJCsiInventorySelectionView.class);
		store.bind(SpellSelectionView.class, LibJCsiSelectSpellView.class);
		store.register(MessageOutput.class);
		inGame.addDrawableView(store.get(MessageOutput.class));
	}

	private void createWorld(final SeedContainer store) throws IOException {
		store.bind(PlayerCharacter.class, Player.class);
		store.bind(DiceBag.class, new SkillRollDice());
		PlayerCharacter pc = store.get(PlayerCharacter.class);
		TargetCreatureSelecor creatureSelecor = new TargetCreatureSelecor(console);
		pc.learnSpell(org.gap.wizards.spells.Factory.getIceball(creatureSelecor));
		pc.addSkill(new BasicSkill("Iceball", 11, store.get(DiceBag.class)));
		pc.addSkill(new BasicSkill("Alchemy", 12, store.get(DiceBag.class)));
		pc.addSkill(new BasicSkill("Foraging", 11, store.get(DiceBag.class)));
		store.register(QuickView.class);
		store.bind(MapContainer.class, World.class);
		store.bind(Location.class, new Builder<Location>() {
			@Override
			public Class<? extends Location> getType() {
				return Location.class;
			}

			@Override
			public Location createInstance() {
				return store.get(MapContainer.class).activeMap();
			}
		});
		MapContainer container = store.get(MapContainer.class);
		if (PersistenceUtils.hasSave()) {
			FlatFilePersistence persister = new FlatFilePersistence(false, "World", "wizard", "save", "map");
			persister.setSeedContainer(store);
			container.load(persister);
			store.get(PlayerCharacter.class).load();
			store.get(TimerState.class).load();			
		} else {
			WorldBuilder.createWorld(store);
		}
	}

	public void run() {
		while (true) {
			console.clear();
			tracker.draw(console);
			console.refresh();
			int key = getKey();
			CharKey charKey = new CharKey(key);
//			if (charKey.isMetaKey()) {
				if (charKey.code == CharKey.F10) {
					String property = System.getProperty("show.all");
					if (property == null) 
						System.setProperty("show.all", "true");
					else 
						System.setProperty("show.all", null);
				}
//			}
			char keyCharacter = charKey.toString().charAt(0);
			tracker.acceptKey(keyCharacter);
			if (tracker.isEndState()) {
				performShutdown();
				break;
			}
			keyProcessed();
		}
		keyProcessed();
		listeners.clear();
		System.exit(0);
	}

	private int getKey() {
		waitForInput();
		int readInput = console.readInput();
		inputReceived(readInput);
		return readInput;
	}

	private void waitForInput() {
		for (GameListener each : listeners) {
			each.waitForInput();
		}
	}

	private void inputReceived(int readInput) {
		for (GameListener each : listeners) {
			each.inputReceived(readInput);
		}
	}

	private void keyProcessed() {
		for (GameListener each : listeners) {
			each.keyProcessed();
		}
	}

	private void performShutdown() {
		for (GameListener each : listeners) {
			each.gameShutdown();
		}
		isClosed = true;
	}

	public static boolean isClosed() {
		return isClosed;
	}
}
